﻿using System;
using System.Drawing;
using System.Threading;
using XFControls.Utils;

namespace XFControls.XFPanels.XFPanelItems
{
    public interface IXFItemLoadingWorker : IDisposable
    {
        event EventHandler WorkerDone;
        WorkerSyncState AsyncState { get; set; }
        void DoWork();
    }

    public interface IXFItemUpdatingWorker : IXFItemLoadingWorker 
    {
        Action<string> WorkerUpdate { get; set; }
    }

    public enum WorkerSyncState
    {
        None,
        ForceAsync,
        ForceSync
    }

    public class WorkerEventArgs : EventArgs
    {
        public WorkerEventArgs(IXFItemLoadingWorker _worker)
        {
            Worker = _worker;
        }

        public IXFItemLoadingWorker Worker { get; set; }
    }

    public class XFItemLoading : XFItemBase
    {
        public bool WaitTillFinishSlide;
        public bool Async = true;
        private Thread _sharedThread;
        public delegate void WorkEventHandler(object sender, WorkerEventArgs e);

        private readonly IXFItemLoadingWorker _worker;

        private bool _hasWorked;
        private int height = 50;
        private EventHandler _workerDone;
        private EventHandler _finishSlide;
        private string _status = "Loading...";

        public XFItemLoading(IXFItemLoadingWorker worker, bool visible)
            : this(worker)
        {
            if (!visible)
                height = 0;
        }

        public XFItemLoading(IXFItemLoadingWorker worker)
            : this()
        {
            _worker = worker;

            // If this worker gives us updates on it's progress, update the display
            if (worker is IXFItemUpdatingWorker)
                ((IXFItemUpdatingWorker)worker).WorkerUpdate = UpdateState;
        }

        public XFItemLoading()
        {
            _hasWorked = false;

            Style = new XFItemStyle
                    {
                        BackgroundColor = Color.White,
                        TextColor = Color.White,
                        TextFont = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Bold)
                    };
            ItemType = XFItemType.Display;

            switch (XFGraphics.GetScreenResolution())
            {
                case ScreenResolution.QVGA:
                case ScreenResolution.RotatedQVGA:
                case ScreenResolution.Unknown:
                    height /= 2;
                    break;
            }

            _workerDone = (o, e) =>
            {
                if (WorkFinished != null)
                    WorkFinished(this, new WorkerEventArgs(_worker));

                height = 0;

                if (Parent.InvokeRequired)
                {
                    if (!Parent.IsDisposed)
                    {
                        Parent.Damage(true);
                    }
                }
                else
                    Parent.Invalidate();
            };
        }

        public event WorkEventHandler WorkFinished;

        public override int GetHeight()
        {
            if (Buffer == null && height > 0)
                Buffer = XFBitmapFactory.GetBitmap(GetWidth(false), height);

            return height;
        }

        public void UpdateState(string state)
        {
            _hasWorked = true;
            _status = state;
            Invalidate(true, ThreadPriority.Normal);
        }

        public override void ItemPaint(Graphics g, int x, int y)
        {
            base.ItemPaint(g, x, y);
            if (!_hasWorked)
            {
                DoWork();
            }
            int width = GetWidth();

            DrawUI("loading_bg", g, new Rectangle(x, y, width, height));

            int xmid = width >> 1;
            if (Style == null)
                return;

            xmid -= (int)(g.MeasureString(_status, Style.TextFont).Width / 2);

            using (var b = new SolidBrush(Style.TextColor))
                g.DrawString(_status, Style.TextFont, b, xmid, y + 2);
        }

        public void DoWork()
        {
            if (_hasWorked)
                return;

            if (WaitTillFinishSlide && Parent != null && Parent.State != States.Idle)
            {
                if(_finishSlide != null) return;

                _finishSlide = (o, e) => DoWork();
                Parent.OnFinishSlide += _finishSlide;
                return;
            }

            if (_finishSlide != null)
                Parent.OnFinishSlide -= _finishSlide;

            _hasWorked = true;

            if (_worker != null && (Async || _worker.AsyncState == WorkerSyncState.ForceAsync) && _worker.AsyncState != WorkerSyncState.ForceSync)
            {
                ThreadStart start = doWork;
                _sharedThread = new Thread(start) { IsBackground = true };

                _sharedThread.Start();
            }
            else
            {
                doWork();
            }
        }

        private void doWork()
        {
            if (_worker == null)
                return;
            
            _worker.WorkerDone += _workerDone;
            _worker.DoWork();
        }

        public override void Dispose()
        {
            base.Dispose();
            WorkFinished = null;

            if (_worker == null) return;
            _worker.Dispose();
        }
    }
}